/**
 * This file is Copyright © 2008 Software Craftsmen Limited. All Rights Reserved.
 */
package com.softwarecraftsmen.cachecontrol;

import com.softwarecraftsmen.cachecontrol.cacheFactories.CacheFactory;
import com.softwarecraftsmen.cachecontrol.cacheFactories.UnsupportedCacheFactory;
import com.softwarecraftsmen.cachecontrol.caches.ThreadSafeHashMapCache;
import com.softwarecraftsmen.functors.NullaryReturningFunctor;
import com.softwarecraftsmen.functors.UnaryFunctor;
import com.softwarecraftsmen.functors.UnaryReturningFunctor;
import com.softwarecraftsmen.cachecontrol.keys.IdentityKey;
import com.softwarecraftsmen.cachecontrol.keys.Key;
import org.jetbrains.annotations.NotNull;

public class Cashier implements CacheDispenser, Comptroller
{
	private static final CacheFactory unsupportedCacheFactory = new UnsupportedCacheFactory();
	private final ThreadSafeHashMapCache<ComptrolledCache<?>> knownCaches;
	private final CacheFactory cacheFactory;

	public Cashier()
	{
		this(unsupportedCacheFactory);
	}

	public Cashier(@NotNull final CacheFactory cacheFactory)
	{
		this.cacheFactory = cacheFactory;
		IdentityKey<ComptrolledCache<?>> knownCachesKey = IdentityKey.unique();
		knownCaches = new ThreadSafeHashMapCache<ComptrolledCache<?>>(knownCachesKey);
	}

	@SuppressWarnings({"unchecked", "ConstantConditions"})
	public @NotNull <V> Cache<V> dispense(@NotNull final Key<V> cacheIdentifier)
	{
		final Item<ComptrolledCache<V>> cacheItem = cacheItem(cacheIdentifier);

		return cacheItem.ternary
		(
			new UnaryReturningFunctor<ComptrolledCache<V>, Cache<V>>(){@SuppressWarnings({"ConstantConditions"}) public @NotNull Cache<V> closure(@NotNull final ComptrolledCache<V> value){

				return cacheItem.value();

			}},new NullaryReturningFunctor<Cache<V>>(){@SuppressWarnings({"ConstantConditions"}) public @NotNull Cache<V> closure(){

				return cacheItem.replace(cacheFactory.create(cacheIdentifier)).value();
			}}
		);
	}

	@SuppressWarnings({"unchecked", "ConstantConditions"})
	private <V> Item<ComptrolledCache<V>> cacheItem(final Key<V> identifier)
	{
		final Object item = knownCaches.obtain(identifier);
		return (Item<ComptrolledCache<V>>) item;
	}

	public <V> void invalidateCache(@NotNull final Key<V> cacheIdentifier)
	{
		cacheItem(cacheIdentifier).hasValue(new UnaryFunctor<ComptrolledCache<V>>()
		{
			public void closure(@NotNull final ComptrolledCache<V> value)
			{
				value.invalidate();
			}
		});
	}

	public void invalidateAllCaches()
	{
		knownCaches.forEach(invalidateAllItemsClosure);
	}

	private static final UnaryFunctor<CacheItem<ComptrolledCache<?>>> invalidateAllItemsClosure = new UnaryFunctor<CacheItem<ComptrolledCache<?>>>()
	{
		public void closure(@NotNull final CacheItem<ComptrolledCache<?>> value)
		{
			value.hasValue(valueInvalidatorClosure);
		}
	};

	private static final UnaryFunctor<ComptrolledCache<?>> valueInvalidatorClosure = new UnaryFunctor<ComptrolledCache<?>>()
	{
		public void closure(@NotNull final ComptrolledCache<?> value)
		{
			value.invalidate();
		}
	};
}
